package com.runvp.service.app;

import com.runvp.common.config.RunvpConfig;
import com.runvp.common.constant.ConstantKey;
import com.runvp.common.core.redis.DistributeRedisLock;
import com.runvp.common.core.redis.RedisCache;
import com.runvp.common.core.redis.RedisLockClient;
import com.runvp.common.enums.*;
import com.runvp.common.exception.base.BaseException;
import com.runvp.common.model.vo.app.BizCreateOrderVO;
import com.runvp.common.model.vo.order.BizOrderOutEndVO;
import com.runvp.common.util.ToolUtils;
import com.runvp.common.utils.DateUtils;
import com.runvp.common.utils.PageUtils;
import com.runvp.common.utils.Threads;
import com.runvp.common.utils.file.FileUtils;
import com.runvp.common.utils.uuid.IdUtils;
import com.runvp.common.utils.validtion.CheckException;
import com.runvp.service.biz.domain.*;
import com.runvp.service.biz.handler.WechatPayHandler;
import com.runvp.service.biz.mapper.*;
import com.runvp.service.biz.service.IBizCustomerWalletService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * app Service业务层处理
 *
 * @author runvp
 * @date 2022-09-18
 */
@Slf4j
@Service
public class AppClientService {

    @Autowired
    private BizSiteMapper bizSiteMapper;
    @Autowired
    private BizCustomerMapper bizCustomerMapper;
    @Autowired
    private BizDeviceMapper bizDeviceMapper;
    @Autowired
    private BizDeviceCargoMapper bizDeviceCargoMapper;
    @Autowired
    private BizOrderMapper bizOrderMapper;

    @Autowired
    private WechatPayHandler wechatPayHandler;
    @Autowired
    private IBizCustomerWalletService bizCustomerWalletService;

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RedisLockClient lockClient;

    /**
     * 充电成功回调
     *
     * @param endVO
     */
    public void outEnd(BizOrderOutEndVO endVO) {
        Threads.sleep(1500);
        String orderNo = endVO.getOrderNo();
        //订单信息
        BizOrder order = bizOrderMapper.selectBizOrderByOrderNo(orderNo);
        if (null == order) {
            throw new BaseException("订单【" + endVO.getOrderNo() + "】不存在");
        }
        if (OrderStatus.COMPLETED.getCode().equals(order.getStatus())) {
            throw new BaseException("订单【" + endVO.getOrderNo() + "】已完成充电");
        }
        if (!OrderPayStatus.PAY_SUCCESS.getCode().equals(order.getPayStatus())) {
            throw new BaseException("订单【" + endVO.getOrderNo() + "】未完成支付");
        }
        String msg;
        String os = "";
        String osr = String.format("\n时间【%s】充电完毕" , DateUtils.getTime());
        order.setRemark(order.getRemark()+osr);
        order.setSuccessTime(new Date());
        order.setStatus(OrderStatus.COMPLETED.getCode());
        order.setUpdateTime(new Date());
        bizOrderMapper.updateBizOrder(order);
        //删除支付二维码
        FileUtils.deleteFile(RunvpConfig.getProfile() + "/qrcode/" + ConstantKey.QR_CODE + orderNo + ".jpg");
    }
    /**
     * 校验库存
     * @param item
     */
    public boolean checkGoodsStock(BizOrderItem item){
        DistributeRedisLock redisLock = lockClient.getRedisLock("deductGoodsStock:"+item.getDeviceSn());
        try {
            redisLock.lock();
            int number = ToolUtils.toInt(item.getDevicePort());
            BizDeviceCargo cargo = bizDeviceCargoMapper.selectBizDeviceCargoBySnAndNumber(new BizDeviceCargo(item.getDeviceSn(),number));
            if (ToolUtils.toInt(cargo.getGoodsStock()) < item.getGoodsNum()) {
                throw new BaseException("【" + cargo.getGoodsName() + "】库存不足");
            }
            return true;
        } catch (Exception e){
            log.error("校验库存:{}",e.getMessage());
            return false;
        }finally {
            redisLock.unlock();
        }
    }
    /**
     * 扣除库存
     * @param item
     */
    public boolean deductGoodsStock(String createBy,BizOrderItem item){
        DistributeRedisLock redisLock = lockClient.getRedisLock("deductGoodsStock:"+item.getDeviceSn());
        try {
            redisLock.lock();
            int number = ToolUtils.toInt(item.getDevicePort());
            BizDeviceCargo cargo = bizDeviceCargoMapper.selectBizDeviceCargoBySnAndNumber(new BizDeviceCargo(item.getDeviceSn(),number));
            if (ToolUtils.toInt(cargo.getGoodsStock()) < item.getGoodsNum()) {
                throw new BaseException("【" + cargo.getGoodsName() + "】库存不足");
            }
            //扣除库存
            cargo.setGoodsStock(ToolUtils.toInt(cargo.getGoodsStock()) - item.getOutSuccessNum());
            cargo.setUpdateBy(createBy);
            bizDeviceCargoMapper.updateBizDeviceCargo(cargo);
            return true;
        } catch (Exception e){
            log.error("扣除库存失败:{}",e.getMessage());
            return false;
        }finally {
            redisLock.unlock();
        }
    }

    /**
     * 根据设备查询设备端口
     *
     * @param sn
     */
    public List<BizDeviceCargoVO> deviceGoods(String sn) {
        BizDevice bizDevice = getBizDeviceBySn(sn);
        BizDeviceCargo query = new BizDeviceCargo();
        query.setDeviceId(bizDevice.getId());
        PageUtils.orderBy(" number asc ");
        List<BizDeviceCargoVO> vos = new ArrayList<>();
        List<BizDeviceCargo> cargos = bizDeviceCargoMapper.selectBizDeviceCargoList(query);
        for (BizDeviceCargo cargo : cargos) {
            BizDeviceCargoVO vo = new BizDeviceCargoVO();
            BeanUtils.copyProperties(cargo,vo);
            if (EnableStatus.DISABLE.getCode().equals(cargo.getStatus())) {
                vo.setRemark("端口未启用");
            }
            vo.setRemark(CargoUseStatus.getDescByCode(cargo.getUseStatus()));
            //启用，无故障，为占用的端口才可使用
            if(EnableStatus.ENABLE.getCode().equals(cargo.getStatus()) && CargoUseStatus.IDLE.getCode().equals(cargo.getUseStatus())){
                vo.setUsabled(true);
            }
            vos.add(vo);
        }
        return vos;
    }

    /**
     * 订单创建
     *
     * @param createOrderVO
     */
    public Map<String, Object> createOrder(BizCreateOrderVO createOrderVO) {
        DistributeRedisLock redisLock = lockClient.getRedisLock("createOrder:" + createOrderVO.getDeviceId());
        try {
            redisLock.lock();
            return createOrderBase(createOrderVO);
        } finally {
            redisLock.unlock();
        }
    }

    /**
     * 生成订单号
     * @param createOrderVO
     * @return str
     */
    private String buildOrderNo(BizCreateOrderVO createOrderVO) {
        return String.valueOf(IdUtils.id());
    }

    /**
     * 获取支付类型
     * @param createOrderVO
     * @return OrderPayMethod
     */
    private OrderPayMethod getPayMethod(BizCreateOrderVO createOrderVO){
        OrderPayMethod payMethod = OrderPayMethod.PAY_WECHAT;
        if (PayChannel.PAY_WECHAT.getCode().equals(createOrderVO.getPayChannel())) {
            payMethod = OrderPayMethod.PAY_WECHAT;
        } else if (PayChannel.PAY_QR.getCode().equals(createOrderVO.getPayChannel())) {
            if (PayChannel.PAY_WECHAT.getCode().equals(createOrderVO.getPayPlat())) {
                payMethod = OrderPayMethod.PAY_QR_WECHAT;
            } else if (PayChannel.PAY_ALIPAY.getCode().equals(createOrderVO.getPayPlat())) {
                payMethod = OrderPayMethod.PAY_QR_ALIPAY;
            }
        } else if (PayChannel.PAY_SCORE.getCode().equals(createOrderVO.getPayChannel())) {
            payMethod = OrderPayMethod.PAY_SCORE;
        }else if(PayChannel.PAY_CLOUD_CARD.getCode().equals(createOrderVO.getPayChannel())) {
            payMethod = OrderPayMethod.PAY_CLOUD_CARD;
        }
        return payMethod;
    }

    /**
     * 创建订单
     *
     * @param createOrderVO
     * @return map
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> createOrderBase(BizCreateOrderVO createOrderVO) {
        Long customerId = createOrderVO.getCustomerId();
        String createBy = String.valueOf(customerId);
        String orderNo = buildOrderNo(createOrderVO);
        //todo 检查消费者是否存在未完成订单
//        checkOldOrder(customerId);
        BizOrder order = new BizOrder();
        order.setAmount(createOrderVO.getOa());
        order.setSource(createOrderVO.getSource());
        order.setType(createOrderVO.getPayChannel());
        order.setCustomerId(customerId);
        order.setOrderNo(orderNo);
        handleAmount(order);
        //设置设备信息
        BizDevice device = bizDeviceMapper.selectBizDeviceById(createOrderVO.getDeviceId());
        CheckException.emptyThrowException(device, "设备不可用");
        if (!EnableStatus.ENABLE.getCode().equals(device.getStatus())) {
            throw new BaseException("设备不可用");
        }
        if (!LineStatus.ON_LINE.getCode().equals(device.getLineStatus())) {
            throw new BaseException("设备连接中,请稍后...");
        }
        //设置商品信息
        setGoodsInfo(createOrderVO.getCargoId(), order);
        // 设置消费者信息
        setCustomerInfo(order);
        //设置设备信息
        setDeviceInfo(device, order);

        order.setCreateBy(createBy);
        order.setCreateTime(DateUtils.getNowDate());
        Map<String, Object> map = buildPay();
        map.put("deviceSn", order.getDeviceSn());
        map.put("order", order);
        map.put("orderNo", orderNo);
        map.put("amount", order.getAmount());
        order.setStatus(OrderStatus.GO_ON.getCode());
        order.setEnable(EnableStatus.DISABLE.getCode());
        bizOrderMapper.insertBizOrder(order);
//        redisCache.lPush(ConstantRedisKey.ORDER_PAY_LIST,orderNo,86400);
        return map;
    }

    /**
     * 消费记录
     * @param order
     */
    @Transactional(rollbackFor = Exception.class)
    public void buildCustomerTranLog(String opMsg,BizOrder order,PayChannel payChannel){
        //余额扣款
        BizCustomerTranLog bizCustomerTranLog = new BizCustomerTranLog();
        bizCustomerTranLog.setCustomerId(order.getCustomerId());
        bizCustomerTranLog.setTranFee(order.getAmount());
        bizCustomerTranLog.setPayFee(order.getAmount());
        bizCustomerTranLog.setTranNo(order.getOrderNo());
        bizCustomerTranLog.setCreateBy(order.getCreateBy());
        bizCustomerTranLog.setPayChannel(payChannel.getCode());
        bizCustomerTranLog.setBillChangeType(BillChangeType.OUT.getCode());
        bizCustomerTranLog.setTranType(TranType.BUY.getCode());
        bizCustomerTranLog.setRemark(TranType.BUY.getDesc());
        bizCustomerWalletService.rechargeSave(opMsg, bizCustomerTranLog);
    }
    /**
     * 设置设备出液口
     *
     * @param order
     * @param device
     * @param goodsId
     */
    private void setDevicePort(BizOrder order, BizDevice device, Long goodsId) {
        throw new BaseException("未开启的出液口");
    }

    /**
     * 校验是否存在未完成订单
     */
    private void checkOldOrder(Long customerId) {
        BizOrder query = new BizOrder();
        query.setStatus(OrderStatus.GO_ON.getCode());
        query.setEnable(EnableStatus.ENABLE.getCode());
        query.setCustomerId(customerId);
        List<BizOrder> orders = bizOrderMapper.selectBizOrderList(query);
        if (!CollectionUtils.isEmpty(orders)) {
            throw new BaseException("请先处理未完成订单");
        }
    }

    /**
     * 构建支付细腻些
     *
     * @return map
     */
    private Map<String, Object> buildPay() {
        Map<String, Object> map = new HashMap<>();
        map.put("status", "success");
        return map;
    }

    /**
     * 设置消费者信息
     *
     * @param order
     */
    private void setCustomerInfo(BizOrder order) {
        if (order.getCustomerId() == 1L) {
            log.info("app下单");
            order.setCustomerPhone("默认用户");
            return;
        }
        BizCustomer customer = bizCustomerMapper.selectBizCustomerById(order.getCustomerId());
        if (null == customer) {
            //用户信息不存在，则临时设置登录中的用户信息
            log.error("订单【{}】中消费者【{}】信息不存在", order.getOrderNo(), order.getCustomerId());
            return;
        }
        order.setCustomerId(customer.getId());
        order.setCustomerAccount(customer.getOpenId());
        order.setCustomerPhone(customer.getPhone());
    }

    /**
     * 设置设备信息
     *
     * @param device
     * @param order
     */
    private void setDeviceInfo(BizDevice device, BizOrder order) {
        setSiteInfo(device, order);
        order.setDeviceId(device.getId());
        order.setDeviceSn(device.getDeviceCode());
        order.setDeviceDiscount(device.getDiscount());
        order.setDeviceType(device.getDeviceType());
        //订单所属跟着设备走
        order.setTenantId(device.getTenantId());
    }

    /**
     * 订单金额处理
     * @param order
     */
    private void handleAmount(BizOrder order){
        //查询用户余额
        BizCustomerWallet wallet = bizCustomerWalletService.customerWallet(order.getCustomerId());
        if(PayChannel.PAY_SCORE.getCode().equals(order.getType())){
            //正常订单校验余额
            if (wallet.getBalance().doubleValue() <= 0) {
                log.error("用户【{}】余额不足,无法开启充电",order.getCustomerId());
                throw new BaseException("余额不足,无法开启充电!");
            }
            order.setAmount(wallet.getBalance());
        }
    }
    /**
     * 设置商品信息 计算订单金额
     *
     * @param cargoId
     * @param order
     */
    @Transactional(rollbackFor = Exception.class)
    public void setGoodsInfo(Long cargoId, BizOrder order) {
        BizDeviceCargo cargo = bizDeviceCargoMapper.selectBizDeviceCargoById(cargoId);
        if (null == cargo) {
            log.error("端口【{}】不存在",cargoId);
            return;
        }
        if(CargoUseStatus.BREAKDOWN.getCode().equals(cargo.getUseStatus())){
            throw new BaseException("端口故障");
        }
        if(CargoUseStatus.USE.getCode().equals(cargo.getUseStatus())){
            throw new BaseException("端口使用中");
        }
        order.setPort(cargo.getNumber());
        order.setCargoId(cargo.getId());
        order.setNum(1);
    }

    /**
     * 设置单价信息
     *
     * @param source
     * @param order
     */
    private void setSiteInfo(BizDevice source, BizOrder order) {
        BizSite bizSite = bizSiteMapper.selectBizSiteById(ToolUtils.strTOLong(source.getPosition()));
        if (null == bizSite) {
            log.error("订单【{}】中设备【{}】未设置场地信息", order.getOrderNo(), source.getDeviceCode());
            return;
        }
        order.setSiteId(bizSite.getId());
        order.setSiteName(bizSite.getName());
        order.setSitePosition(bizSite.getPosition());

    }


    /**
     * 根据设备号查询设备信息
     *
     * @param sn
     * @return BizDevice
     */
    private BizDevice getBizDeviceBySn(String sn) {
        BizDevice query = new BizDevice();
        query.setDeviceCode(sn);
        query.setStatus(EnableStatus.ENABLE.getCode());
        List<BizDevice> deviceList = bizDeviceMapper.selectBizDeviceList(query);
        CheckException.emptyListThrowException(deviceList, "设备不可用");
        BizDevice device = deviceList.get(0);
        if (!LineStatus.ON_LINE.getCode().equals(device.getLineStatus())) {
            throw new BaseException("设备连接中,请稍后...");
        }
        return device;
    }

}
